package cn.yemuysy.code.lingting.core;

import cn.yemuysy.code.lingting.model.MatchResult;
import cn.yemuysy.code.lingting.model.PinyinGraph;
import cn.yemuysy.code.lingting.model.TrieNode;
import cn.yemuysy.code.lingting.util.PinyinUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * 拼音敏感词匹配器
 * 实现DFS+AC自动机结合的高级匹配算法，支持谐音词检测
 */
public class PinyinMatcher {
    
    private static final Logger logger = LoggerFactory.getLogger(PinyinMatcher.class);
    
    /**
     * 拼音AC自动机（音节模式）
     */
    private AhoCorasickAutomaton pinyinAutomaton;
    
    /**
     * 原始敏感词到拼音的映射
     */
    private Map<String, String> wordToPinyin;
    
    /**
     * 拼音到原始敏感词的映射
     */
    private Map<String, Set<String>> pinyinToWords;
    
    /**
     * DFS搜索状态
     */
    private static class DFSState {
        /**
         * 当前拼音图节点索引
         */
        int graphNodeIndex;
        
        /**
         * 当前拼音路径索引
         */
        int pinyinIndex;
        
        /**
         * AC自动机当前节点
         */
        TrieNode automatonNode;
        
        /**
         * 已访问的拼音图路径（用于剪枝）
         */
        Set<String> visitedPaths;
        
        public DFSState(int graphNodeIndex, int pinyinIndex, TrieNode automatonNode) {
            this.graphNodeIndex = graphNodeIndex;
            this.pinyinIndex = pinyinIndex;
            this.automatonNode = automatonNode;
            this.visitedPaths = new HashSet<>();
        }
        
        public DFSState copy() {
            DFSState newState = new DFSState(graphNodeIndex, pinyinIndex, automatonNode);
            newState.visitedPaths = new HashSet<>(this.visitedPaths);
            return newState;
        }
        
        public String getPathKey() {
            return graphNodeIndex + "-" + pinyinIndex + "-" + automatonNode.hashCode();
        }
    }
    
    public PinyinMatcher() {
        this.pinyinAutomaton = new AhoCorasickAutomaton(true); // 音节模式
        this.wordToPinyin = new HashMap<>();
        this.pinyinToWords = new HashMap<>();
    }
    
    /**
     * 添加拼音敏感词
     * @param word 原始敏感词
     */
    public void addPinyinWord(String word) {
        if (word == null || word.trim().isEmpty()) {
            return;
        }
        
        // 获取拼音
        String pinyin = PinyinUtil.getFirstPinyin(word);
        if (pinyin.isEmpty()) {
            logger.warn("无法获取拼音: {}", word);
            return;
        }
        
        // 建立映射关系
        wordToPinyin.put(word, pinyin);
        pinyinToWords.computeIfAbsent(pinyin, k -> new HashSet<>()).add(word);
        
        // 添加到拼音AC自动机
        pinyinAutomaton.addWord(pinyin);
        
        logger.debug("添加拼音敏感词: {} -> {}", word, pinyin);
    }
    
    /**
     * 批量添加拼音敏感词
     * @param words 敏感词列表
     */
    public void addPinyinWords(Collection<String> words) {
        if (words == null || words.isEmpty()) {
            return;
        }
        
        for (String word : words) {
            addPinyinWord(word);
        }
        
        logger.info("批量添加拼音敏感词完成，总计: {} 个", words.size());
    }
    
    /**
     * 搜索文本中的拼音敏感词
     * @param text 要搜索的文本
     * @return 匹配结果列表
     */
    public List<MatchResult> searchPinyinWords(String text) {
        if (text == null || text.isEmpty()) {
            return new ArrayList<>();
        }
        
        // 构建拼音图
        PinyinGraph pinyinGraph = PinyinUtil.buildPinyinGraph(text);
        
        if (pinyinGraph.size() == 0) {
            return new ArrayList<>();
        }
        
        // 使用DFS+AC自动机进行匹配
        return dfsSearch(pinyinGraph, text);
    }
    
    /**
     * DFS搜索算法
     * @param pinyinGraph 拼音图
     * @param originalText 原始文本
     * @return 匹配结果列表
     */
    private List<MatchResult> dfsSearch(PinyinGraph pinyinGraph, String originalText) {
        List<MatchResult> results = new ArrayList<>();
        
        if (pinyinGraph.size() == 0) {
            return results;
        }
        
        // 初始化DFS栈
        Stack<DFSState> dfsStack = new Stack<>();
        
        // 为第一个节点的所有拼音创建初始状态
        PinyinGraph.PinyinNode firstNode = pinyinGraph.getNode(0);
        for (String pinyin : firstNode.getPinyins()) {
            DFSState initialState = new DFSState(0, 0, pinyinAutomaton.getRoot());
            dfsStack.push(initialState);
        }
        
        // 全局访问状态记录（用于剪枝优化）
        Set<String> globalVisited = new HashSet<>();
        
        while (!dfsStack.isEmpty()) {
            DFSState currentState = dfsStack.pop();
            
            // 剪枝策略1：避免重复访问相同状态
            String stateKey = currentState.getPathKey();
            if (globalVisited.contains(stateKey)) {
                continue;
            }
            globalVisited.add(stateKey);
            
            // 获取当前节点的拼音列表
            PinyinGraph.PinyinNode currentNode = pinyinGraph.getNode(currentState.graphNodeIndex);
            if (currentNode == null || currentState.pinyinIndex >= currentNode.getPinyinCount()) {
                continue;
            }
            
            String currentPinyin = currentNode.getPinyins().get(currentState.pinyinIndex);
            
            // 在AC自动机中进行状态转移
            TrieNode nextAutomatonNode = findNextNode(currentState.automatonNode, currentPinyin);
            
            if (nextAutomatonNode != null) {
                // 检查是否匹配到拼音敏感词
                checkPinyinMatches(nextAutomatonNode, currentState.graphNodeIndex, 
                                 pinyinGraph, originalText, results);
                
                // 继续DFS搜索
                addNextStates(dfsStack, currentState, nextAutomatonNode, pinyinGraph);
            }
            
            // 尝试当前节点的其他拼音
            if (currentState.pinyinIndex + 1 < currentNode.getPinyinCount()) {
                DFSState nextPinyinState = currentState.copy();
                nextPinyinState.pinyinIndex++;
                dfsStack.push(nextPinyinState);
            }
        }
        
        return results;
    }
    
    /**
     * 在AC自动机中查找下一个节点
     */
    private TrieNode findNextNode(TrieNode currentNode, String pinyin) {
        TrieNode node = currentNode;
        
        // 尝试直接转移
        if (node.hasChild(pinyin)) {
            return node.getChild(pinyin);
        }
        
        // 通过Fail指针转移
        while (node != null && !node.hasChild(pinyin)) {
            node = node.getFailPointer();
        }
        
        if (node != null) {
            return node.getChild(pinyin);
        }
        
        return null;
    }
    
    /**
     * 检查拼音匹配
     */
    private void checkPinyinMatches(TrieNode node, int currentPosition, 
                                  PinyinGraph pinyinGraph, String originalText, 
                                  List<MatchResult> results) {
        TrieNode temp = node;
        
        while (temp != null) {
            if (temp.isEndOfWord()) {
                String matchedPinyin = temp.getMatchedWord();
                
                // 获取对应的原始敏感词
                Set<String> originalWords = pinyinToWords.get(matchedPinyin);
                if (originalWords != null) {
                    for (String originalWord : originalWords) {
                        // 计算匹配位置
                        String[] pinyinSyllables = matchedPinyin.trim().split("\\s+");
                        int startPosition = currentPosition - pinyinSyllables.length + 1;
                        
                        if (startPosition >= 0) {
                            MatchResult result = new MatchResult(
                                originalWord, 
                                startPosition, 
                                currentPosition, 
                                MatchResult.MatchType.PINYIN
                            );
                            results.add(result);
                            
                            logger.debug("匹配到拼音敏感词: {} -> {} 位置: {}-{}", 
                                       originalWord, matchedPinyin, startPosition, currentPosition);
                        }
                    }
                }
            }
            temp = temp.getFailPointer();
        }
    }
    
    /**
     * 添加下一步的DFS状态
     */
    private void addNextStates(Stack<DFSState> stack, DFSState currentState, 
                             TrieNode nextAutomatonNode, PinyinGraph pinyinGraph) {
        int nextGraphIndex = currentState.graphNodeIndex + 1;
        
        // 剪枝策略2：深度控制
        if (nextGraphIndex >= pinyinGraph.size()) {
            return;
        }
        
        // 剪枝策略3：路径长度比较
        int automatonDepth = getNodeDepth(nextAutomatonNode);
        int graphBranchLength = nextGraphIndex;
        
        if (automatonDepth < graphBranchLength) {
            return; // 模式串长度短于当前拼音图路径长度
        }
        
        PinyinGraph.PinyinNode nextNode = pinyinGraph.getNode(nextGraphIndex);
        if (nextNode != null) {
            // 为下一个节点的所有拼音创建新状态
            for (int i = 0; i < nextNode.getPinyinCount(); i++) {
                DFSState nextState = new DFSState(nextGraphIndex, i, nextAutomatonNode);
                stack.push(nextState);
            }
        }
    }
    
    /**
     * 计算AC自动机节点的深度
     */
    private int getNodeDepth(TrieNode node) {
        // 简化实现：通过匹配词的长度来估算深度
        if (node.isEndOfWord()) {
            String word = node.getMatchedWord();
            return word.trim().split("\\s+").length;
        }
        
        // 向上遍历寻找最近的有效节点
        TrieNode temp = node;
        int depth = 0;
        while (temp != null && temp.getFailPointer() != null) {
            depth++;
            temp = temp.getFailPointer();
            if (temp.isEndOfWord()) {
                String word = temp.getMatchedWord();
                return depth + word.trim().split("\\s+").length;
            }
        }
        
        return depth;
    }
    
    /**
     * 检查文本是否包含拼音敏感词
     * @param text 要检查的文本
     * @return true如果包含拼音敏感词
     */
    public boolean containsPinyinWord(String text) {
        List<MatchResult> results = searchPinyinWords(text);
        return !results.isEmpty();
    }
    
    /**
     * 获取拼音敏感词数量
     */
    public int getPinyinWordCount() {
        return wordToPinyin.size();
    }
    
    /**
     * 获取指定敏感词的拼音
     * @param word 敏感词
     * @return 拼音字符串，不存在时返回null
     */
    public String getWordPinyin(String word) {
        return wordToPinyin.get(word);
    }
    
    /**
     * 获取指定拼音对应的所有敏感词
     * @param pinyin 拼音字符串
     * @return 敏感词集合
     */
    public Set<String> getWordsForPinyin(String pinyin) {
        return pinyinToWords.getOrDefault(pinyin, new HashSet<>());
    }
    
    /**
     * 移除拼音敏感词
     * @param word 要移除的敏感词
     * @return 是否成功移除
     */
    public boolean removePinyinWord(String word) {
        String pinyin = wordToPinyin.remove(word);
        if (pinyin != null) {
            Set<String> words = pinyinToWords.get(pinyin);
            if (words != null) {
                words.remove(word);
                if (words.isEmpty()) {
                    pinyinToWords.remove(pinyin);
                    // 重建AC自动机
                    rebuildPinyinAutomaton();
                }
            }
            
            logger.debug("移除拼音敏感词: {} -> {}", word, pinyin);
            return true;
        }
        return false;
    }
    
    /**
     * 重建拼音AC自动机
     */
    private void rebuildPinyinAutomaton() {
        pinyinAutomaton.clear();
        pinyinAutomaton.addWords(pinyinToWords.keySet());
    }
    
    /**
     * 清空所有拼音敏感词
     */
    public void clear() {
        wordToPinyin.clear();
        pinyinToWords.clear();
        pinyinAutomaton.clear();
        logger.info("拼音敏感词匹配器已清空");
    }
    
    @Override
    public String toString() {
        return "PinyinMatcher{" +
                "pinyinWordCount=" + wordToPinyin.size() +
                ", pinyinPatternCount=" + pinyinToWords.size() +
                ", automatonWords=" + pinyinAutomaton.getWordCount() +
                '}';
    }
} 